msg_tool\scripts\yuris/
yser.rs1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug)]
13pub struct YSERBuilder {}
14
15impl YSERBuilder {
16 pub const fn new() -> Self {
18 YSERBuilder {}
19 }
20}
21
22impl ScriptBuilder for YSERBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Cp932
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script + Send + Sync>> {
36 Ok(Box::new(YSER::new(MemReader::new(buf), encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["ybn"]
41 }
42
43 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
44 if buf_len >= 4 && buf.starts_with(b"YSER") {
45 return Some(20);
46 }
47 None
48 }
49
50 fn script_type(&self) -> &'static ScriptType {
51 &ScriptType::YurisYSER
52 }
53
54 fn can_create_file(&self) -> bool {
55 true
56 }
57
58 fn create_file<'a>(
59 &'a self,
60 filename: &'a str,
61 writer: Box<dyn WriteSeek + 'a>,
62 encoding: Encoding,
63 file_encoding: Encoding,
64 config: &ExtraConfig,
65 ) -> Result<()> {
66 create_file(
67 filename,
68 writer,
69 encoding,
70 file_encoding,
71 config.custom_yaml,
72 )
73 }
74}
75
76#[derive(Debug, StructPack, StructUnpack, Deserialize, Serialize)]
77struct StringData {
78 unk: u32,
79 #[cstring]
80 s: String,
81}
82
83#[derive(Debug, StructPack, StructUnpack, Deserialize, Serialize)]
84struct YSERData {
85 engine: u32,
86 #[pvec(u64)]
87 strings: Vec<StringData>,
88}
89
90#[derive(Debug)]
91pub struct YSER {
92 data: YSERData,
93 custom_yaml: bool,
94}
95
96impl YSER {
97 pub fn new<T: Read + Seek>(
98 mut reader: T,
99 encoding: Encoding,
100 config: &ExtraConfig,
101 ) -> Result<Self> {
102 let mut sig = [0; 4];
103 reader.read_exact(&mut sig)?;
104 if &sig != b"YSER" {
105 anyhow::bail!("Unsupported YSER file.");
106 }
107 let data = YSERData::unpack(&mut reader, false, encoding, &None)?;
108 Ok(Self {
109 data,
110 custom_yaml: config.custom_yaml,
111 })
112 }
113}
114
115impl Script for YSER {
116 fn default_output_script_type(&self) -> OutputScriptType {
117 OutputScriptType::Custom
118 }
119
120 fn is_output_supported(&self, output: OutputScriptType) -> bool {
121 matches!(output, OutputScriptType::Custom)
122 }
123
124 fn default_format_type(&self) -> FormatOptions {
125 FormatOptions::None
126 }
127
128 fn custom_output_extension(&self) -> &'static str {
129 if self.custom_yaml { "yaml" } else { "json" }
130 }
131
132 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
133 let s = if self.custom_yaml {
134 serde_yaml_ng::to_string(&self.data)
135 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
136 } else {
137 serde_json::to_string_pretty(&self.data)
138 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
139 };
140 let mut writer = crate::utils::files::write_file(filename)?;
141 let s = encode_string(encoding, &s, false)?;
142 writer.write_all(&s)?;
143 writer.flush()?;
144 Ok(())
145 }
146
147 fn custom_import<'a>(
148 &'a self,
149 custom_filename: &'a str,
150 file: Box<dyn WriteSeek + 'a>,
151 encoding: Encoding,
152 output_encoding: Encoding,
153 ) -> Result<()> {
154 create_file(
155 custom_filename,
156 file,
157 encoding,
158 output_encoding,
159 self.custom_yaml,
160 )
161 }
162}
163
164fn create_file<'a>(
165 custom_filename: &'a str,
166 mut writer: Box<dyn WriteSeek + 'a>,
167 encoding: Encoding,
168 output_encoding: Encoding,
169 yaml: bool,
170) -> Result<()> {
171 let input = crate::utils::files::read_file(custom_filename)?;
172 let s = decode_to_string(output_encoding, &input, true)?;
173 let data: YSERData = if yaml {
174 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
175 } else {
176 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
177 };
178 writer.write_all(b"YSER")?;
179 data.pack(&mut writer, false, encoding, &None)?;
180 Ok(())
181}